/*
 * Copyright (C) 2012 Rothens
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package rpg;

import java.util.ArrayList;
import java.util.List;
import rpg.Tiles.RoadTile;
import rpg.Tiles.Tile;
import rpg.entities.Entity;
import rpg.gfx.Bitmap;
import rpg.util.LineReader;

/**
 * rothens.tumblr.com
 * @author Rothens
 */
public class Maps {

	/**
	 * This is a class that contains all tile layers.
	 */
	public class MapTile {

		private Tile landscape = null;
		private RoadTile road = null;
		private Tile layer1 = null;
		private Tile layer2 = null;

		public MapTile() {
		}

		public MapTile(Tile landscape) {
			this.landscape = landscape;
		}

		public void setRoad(RoadTile road) {
			this.road = road;
		}

		public RoadTile getRoad() {
			return road;
		}

		public void setLandcape(Tile landscape) {
			this.landscape = landscape;
		}

		public void setLayer1(Tile layer1) {
			this.layer1 = layer1;
		}

		public void setLayer2(Tile layer2) {
			this.layer2 = layer2;
		}

		public Tile getLandscape() {
			return landscape;
		}

		public Tile getLayer1() {
			return layer1;
		}

		public Tile getLayer2() {
			return layer2;
		}

		public boolean isPassable() {

			return (road != null)
				|| ((landscape == null || landscape.passable)
				&& (layer1 == null || layer1.passable)
				&& (layer2 == null || layer2.passable));
		}
	}
	Tiles tileset = new Tiles();

	public class Map {

		int width;
		int height;
		public MapTile[] tiles;
		public List<Entity> entities;
		Location spawn = null;
		//EntityHandler eh = new EntityHandler();
		//public Bitmap whole;

		public Map(int width, int height, String mapdata, String layer1data, String layer2data, String roadData, String entityData) {
			this.width = width;
			this.height = height;
			processMap(mapdata);
			processLayer1(layer1data);
			processLayer2(layer2data);
			processRoads(roadData);
			processEntities(entityData);
		}

		public int getWidth() {
			return width;
		}

		public int getHeight() {
			return height;
		}

		private void processMap(String data) {
			int i = 0;
			tiles = new MapTile[width * height];
			for (char c : data.toCharArray()) {
				tiles[i] = new MapTile(tileset.getTile(c));
				i++;
				if (i == width * height) {
					break;
				}
			}
			if (i < width * height - 1) {
				for (; i < width * height; i++) {
					tiles[i] = new MapTile(tileset.getTile('~'));
				}
			}
		}

		public Bitmap getCorner(int i) {
			Bitmap ret = new Bitmap(64, 64);
			Tile act = tiles[i].landscape;
			Tile[] neighbors = new Tile[4];
			int sum = 0;
			for (int j = 0; j < 4; j++) {
				neighbors[j] = getTile(i, j, this).landscape;
				if (neighbors[j].name == act.name || neighbors[j].name == '~') {

					sum += Math.pow(2, j);
				}
			}


			switch (sum) {
				case 15:
					ret = ((Tiles.MultiTile) tiles[i].landscape).bitmaps[4];
					return ret;
				case 14:
					ret.draw(getTile(i, UP, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[1], 0, 0);
					return ret;
				case 13:
					ret.draw(getTile(i, LEFT, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[3], 0, 0);
					return ret;
				case 12:
					ret.draw(getTile(i, LEFT, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[0], 0, 0);
					return ret;
				case 11:
					ret.draw(getTile(i, DOWN, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[7], 0, 0);
					return ret;
				case 9:
					ret.draw(getTile(i, DOWN, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[6], 0, 0);
					return ret;
				case 6:
					ret.draw(getTile(i, UP, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[2], 0, 0);
					return ret;
				case 7:
					ret.draw(getTile(i, RIGHT, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[5], 0, 0);
					return ret;
				case 3:
					ret.draw(getTile(i, DOWN, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[8], 0, 0);
					return ret;

				default:
					ret = ((Tiles.MultiTile) tiles[i].landscape).bitmaps[4];
					break;
			}





			return ret;
		}

		public Bitmap getCornerRoad(int i) {
			Bitmap ret = new Bitmap(64, 64);
			Tile act = tiles[i].landscape;
			Tile[] neighbors = new Tile[4];
			int sum = 0;
			for (int j = 0; j < 4; j++) {
				neighbors[j] = getTile(i, j, this).landscape;
				if (neighbors[j].name == act.name || neighbors[j].name == '~') {

					sum += Math.pow(2, j);
				}
			}


			switch (sum) {
				case 15:
					ret = ((Tiles.MultiTile) tiles[i].landscape).bitmaps[4];
					return ret;
				case 14:
					ret.draw(getTile(i, UP, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[1], 0, 0);
					return ret;
				case 13:
					ret.draw(getTile(i, LEFT, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[3], 0, 0);
					return ret;
				case 12:
					ret.draw(getTile(i, LEFT, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[0], 0, 0);
					return ret;
				case 11:
					ret.draw(getTile(i, DOWN, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[7], 0, 0);
					return ret;
				case 9:
					ret.draw(getTile(i, DOWN, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[6], 0, 0);
					return ret;
				case 6:
					ret.draw(getTile(i, UP, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[2], 0, 0);
					return ret;
				case 7:
					ret.draw(getTile(i, RIGHT, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[5], 0, 0);
					return ret;
				case 3:
					ret.draw(getTile(i, DOWN, this).landscape.bitmap, 0, 0);
					ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[8], 0, 0);
					return ret;

				default:
					ret = ((Tiles.MultiTile) tiles[i].landscape).bitmaps[4];
					break;
			}





			return ret;
		}

		private void processLayer1(String layer1data) {
			int i = 0;

			for (char c : layer1data.toCharArray()) {
				if (c == '.') {
					i++;
					continue;
				}
				if (c == '*' && spawn == null) {
					spawn = new Location(i, this);
				}
				tiles[i].setLayer1(tileset.getLayer1(c));
				i++;
				if (i == width * height) {
					break;
				}
			}
			if (i < width * height - 1) {
				for (; i < width * height; i++) {
					tiles[i].setLayer1(null);
				}
			}
		}

		private void processLayer2(String layer2data) {
			int i = 0;

			for (char c : layer2data.toCharArray()) {
				if (c == '.') {
					i++;
					continue;
				}
				tiles[i].setLayer2(tileset.getLayer2(c));
				i++;
				if (i == width * height) {
					break;
				}
			}
			if (i < width * height - 1) {
				for (; i < width * height; i++) {
					tiles[i].setLayer2(null);
				}
			}
		}

		private void processRoads(String roadData) {
			int i = 0;

			for (char c : roadData.toCharArray()) {
				if (c == '.') {
					i++;
					continue;
				}
				try {
					tiles[i].setRoad((RoadTile) tileset.getRoad(c).clone());
				} catch (Exception ex) {
				}
				i++;
				if (i == width * height) {
					break;
				}
			}
			if (i < width * height - 1) {
				for (; i < width * height; i++) {
					tiles[i].setRoad(null);
				}
			}

			for (i = 0; i < width * height; i++) {
				RoadTile act = tiles[i].getRoad();
				if (act == null) {
					continue;
				}
				int val = 0;
				for (int j = 0; j < 4; j++) {
					RoadTile toCheck = getRoadTile(i, j, this);
					if (toCheck == null) {
						continue;
					}
					if (act.getName() == toCheck.getName()) {
						val += Math.pow(2, j);
					}
				}
				act.setState(val);

			}
			
		}
		
		public boolean isPassableEntity(int tileLoc){
			
			for(Entity e : entities){
				Location l = e.getLocation();
				if(l.getMapCoordinate(l, this) == tileLoc){
					if(!e.isPassable()){
						return false;
					}
				}
			}
			return true;
		}
		
		public List<Entity> getEntity(int tileLoc){
			List<Entity> ret = new ArrayList<Entity>();
			for(Entity e : entities){
				Location l = e.getLocation();
				if(l.getMapCoordinate(l, this) == tileLoc){
					ret.add(e);					
				}
			}
			
			return ret;
		}

		private void processEntities(String entityData) {
			entities = new ArrayList<Entity>();
			int i = 0;

			for (char c : entityData.toCharArray()) {
				if (c == '.') {
					i++;
					continue;
				}

				Entity e = Game.eh.getEntity(c, this, i);
				if (e != null) {
					
					entities.add(e);
				}

				i++;
				if (i == width * height) {
					break;
				}
			}

		}

		private RoadTile getRoadTile(int actual, int dir, Map map) {
			switch (dir) {
				case UP:
					if (actual - map.width < 0) {
						return null;
					}
					return map.tiles[actual - map.width].getRoad();
				case LEFT:
					if (actual % map.width == 0) {
						return null;
					}
					return map.tiles[actual - 1].getRoad();
				case DOWN:
					if (actual + map.width >= map.width * map.height) {
						return null;
					}
					return map.tiles[actual + map.width].getRoad();
				case RIGHT:
					if (actual % map.width == map.width - 1) {
						return null;
					}
					return map.tiles[actual + 1].getRoad();
			}
			return null;
		}
	}
	public static final int UP = 0; //1
	public static final int LEFT = 1; //2
	public static final int DOWN = 2; //4
	public static final int RIGHT = 3; //8

	public MapTile getTile(int actual, int dir, Map map) {
		switch (dir) {
			case UP:
				if (actual - map.width < 0) {
					return map.tiles[actual];
				}
				return map.tiles[actual - map.width];
			case LEFT:
				if (actual % map.width == 0) {
					return map.tiles[actual];
				}
				return map.tiles[actual - 1];
			case DOWN:
				if (actual + map.width >= map.width * map.height) {
					return map.tiles[actual];
				}
				return map.tiles[actual + map.width];
			case RIGHT:
				if (actual % map.width == map.width - 1) {
					return map.tiles[actual];
				}
				return map.tiles[actual + 1];
		}
		return null;
	}

	public Maps() {
		tileset.loadTiles();
	}

	public Map loadMap(String name) {
		Map ret = null;
		LineReader lr = new LineReader(name);
		int w = Integer.parseInt(lr.next());
		if (!lr.hasNext()) {
			return null;
		}
		int h = Integer.parseInt(lr.next());
		String data = "";
		while (lr.hasNext()) {
			String next = lr.next();
			if (next.equals("--")) {
				break;
			}
			data += next;
		}
		String layer1 = "";
		while (lr.hasNext()) {
			String next = lr.next();
			if (next.equals("--")) {
				break;
			}
			layer1 += next;
		}
		String layer2 = "";
		while (lr.hasNext()) {
			String next = lr.next();
			if (next.equals("--")) {
				break;
			}
			layer2 += next;
		}
		String road = "";
		while (lr.hasNext()) {
			String next = lr.next();
			if (next.equals("--")) {
				break;
			}
			road += next;
		}
		String entityData = "";
		while (lr.hasNext()) {
			String next = lr.next();
			if (next.equals("--")) {
				break;
			}
			entityData += next;
		}
		ret = new Map(w, h, data, layer1, layer2, road, entityData);

		return ret;
	}
}
